const ws = require('ws');
const url = require('url');
const HmacSHA256 = require('crypto-js/hmac-sha256');
const Base64 = require('crypto-js/enc-base64');
const UTF8 = require('crypto-js/enc-utf8');

class Spark {
	constructor(SparkConfig, dbContext, SparkVersion) {
		this.Config = SparkConfig;
		this.dbContext = dbContext ?? null;
		this.Ver = SparkVersion ?? 3;
		this._spark_reply = '';
	};

	get reply() { return this._spark_reply };

	Url() {
		let UTC_date = new Date(Date.now()).toUTCString();//获取RFC1123时间 .toLocaleString()
		let tmp = `host: spark-api.xf-yun.com\ndate: ${UTC_date}\nGET /v${this.Ver}.1/chat HTTP/1.1`;// 原始tmp 
		let tmp_sha = HmacSHA256(tmp, this.Config.APISecret).toString();
		let signature = Buffer.from(tmp_sha, 'hex').toString('base64');// 最抽象的一集
		let authorization_origin = `api_key="${this.Config.APIKey}", algorithm="hmac-sha256", headers="host date request-line", signature="${signature}"`;
		let authorization = Base64.stringify(UTF8.parse(authorization_origin));// 最复杂的一集
		// console.log( url.format({
		//   protocol: 'wss',
		//   hostname: 'spark-api.xf-yun.com',
		//   pathname: `/v${this.Ver}.1/chat`,
		//   query: {
		//     authorization: authorization,
		//     date: UTC_date,
		//     host: 'spark-api.xf-yun.com'
		//   }
		// }));

		return url.format({
			protocol: 'wss',
			hostname: 'spark-api.xf-yun.com',
			pathname: `/v${this.Ver}.1/chat`,
			query: {
				authorization: authorization,
				date: UTC_date,
				host: 'spark-api.xf-yun.com'
			}
		});
	};// 获取鉴权URL

	async connectWebSocket() {
		return new Promise((resolve, reject) => {
			const timeout = setTimeout(() => {
				reject('[Spark] SparkAPI 连接超时: 2000ms');
			}, 2000);
			const client = new ws(this.Url());
			client.on('open', () => {
				clearTimeout(timeout);
				console.log('[Spark] SparkAPI 连接成功!')
				client.removeAllListeners();
				resolve(client);
			});
		});
	}

	async getReply(MsgText, callback) {
		if (!this._wsSpark) this._wsSpark = await this.connectWebSocket();
		return new Promise((resolve, reject) => {
			this._wsSpark.on('error', err => reject(err))
			this._wsSpark.on('message', (data, err) => {
				if (err) { reject(err); return }
				if (JSON.parse(data).header.code !== 0) return this._errHandler(JSON.parse(data).header.code);

				this._spark_reply += JSON.parse(data).payload.choices.text[0].content ?? '';
				callback(this._spark_reply);
				if (JSON.parse(data).payload.choices.status === 2) {
					resolve({ content: this._spark_reply, token: JSON.parse(data).payload.usage.text.total_tokens });
				}
			});

			this._wsSpark.send(JSON.stringify(this._getFormatMsg(MsgText)));
		});
	};

	async Ping() {
		let time = Date.now();
		return new Promise((resolve, reject) => {
			setTimeout(() => {
				reject("Timeout: 2000ms.")
			}, 2000);
			this.connectWebSocket()
				.then(() => {
					resolve(Date.now() - time);
				})
				.catch(e => reject(e));
		});
	};

	async _errHandler(code) {
		return new Promise(reject => {
			switch (code) {
				case 10000: case 10001: case 10002: reject({ name: "SparkAPI_WebSocket错误", code }); break;
				case 10003: case 10004: case 10005: reject({ name: "SparkAPI消息格式错误", code }); break;
				case 10013: case 10014: reject({ name: "逆天发言", code }); break;
				case 11201: reject({ name: "日流超限", code }); break;
				case 11202: case 11203: reject({ name: "并发超速", code }); break;
				default: reject({ name: "未知错误", code });
			};
		})
	}

	_getFormatMsg(MsgText) {
		return {
			header: {
				app_id: this.Config.APPID,
			},
			parameter: {
				chat: {
					domain: "general"     // 取值[general, generalv2, generalv3], 对应v1.5, v2, v3版本的spark
						+ (this.Ver === 1 ? "" : "v" + this.Ver.toString()),
					temperature: 0.8,     // 取值[0,1], 取值越高随机性越强
					max_tokens: 2048,     // v1.5, v2, v3取值[1,4096], [1,8192], [1,8192], 模型回答的tokens的最大长度
					top_k: 4,             // 取值为[1,6], 从k个候选中随机选择⼀个
				}
			},
			payload: {
				message: {
					"text": MsgText       // [{ role: "user", content: "" }, { role: "assistant", content: "" }]
				}
			}
		};
	}
};

module.exports = Spark;

// let spark = new Spark(SparkConfig);
// spark.getReply("你好");
